home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / lptalk-1.3 / keyboard.c < prev    next >
C/C++ Source or Header  |  1995-05-03  |  12KB  |  534 lines

  1. /************************************************************************/
  2. /* LP-Talk
  3.     Version 1.0 [ 9/24/90]
  4. 26-Sep-90, shawnm, add do_not_echo for passwords
  5.         1.1 [ 9/27/90]
  6.         1.2 [ 9/28/90]
  7. */
  8. /* TinyTalk keyboard handler.                        */
  9. /*                                    */
  10. /*    Version 1.0 [ 1/24/90] : Initial implementation by ABR.        */
  11. /*        1.1 [ 1/25/90] : Added minimal termcap support.        */
  12. /*        1.2 [ 1/26/90] : Clean up terminal state on ^C.        */
  13. /*        1.3 [ 2/ 2/90] : Allow interrupts to be ignored.    */
  14. /*                 Optionally use stty information.    */
  15. /*        1.4 [ 2/ 5/90] : Add minimal command recall.  Flush log    */
  16. /*                 file on suspend.  Fix problem with gag    */
  17. /*                 causing spurious input erasure.    */
  18. /*        1.5 [ 2/16/90] : Integrated support for System V and    */
  19. /*                 HP-UX, from Andy Norman and Kipp    */
  20. /*                 Hickman.                */
  21. /*        1.6 [ 6/25/90] : Fixed a bug in /logme; thanks to    */
  22. /*                 Jean-Remy Facq for reporting it and    */
  23. /*                 contributing a solution.  Also fixed a    */
  24. /*                 bug in recall of lines starting with    */
  25. /*                 //; thanks to "Insane Hermit" for    */
  26. /*                 reporting this (a LONG time ago).    */
  27. /*                                    */
  28. /************************************************************************/
  29.  
  30. #include "tl.h"
  31. #include <signal.h>
  32.  
  33. #ifdef HPUX
  34.  
  35. #include <sys/ioctl.h>
  36. #include <termio.h>
  37. #include <bsdtty.h>
  38.  
  39. #else /* not HPUX */
  40. #ifdef SYSV
  41.  
  42. #include <termio.h>
  43. #undef TIOCGWINSZ
  44.  
  45. #else /* BSD */
  46.  
  47. #include <sys/ioctl.h>
  48. #include <sgtty.h>
  49.  
  50. #endif /* SYSV */
  51. #endif /* HPUX */
  52.  
  53. #include <errno.h>
  54. #include <stdio.h>
  55.  
  56.   /* For some odd systems, which don't put this in errno.h. */
  57.  
  58. extern int errno;
  59.  
  60. #ifdef TERMCAP
  61. extern char *getenv();
  62. static int have_clear;
  63. static char start_of_line[16], clear_to_eol[16];  /* Warning, short! */
  64. static char write_buffer[64];              /* Also rather short.... */
  65. static char *write_buffer_ptr;
  66. #endif
  67.  
  68. #define DEFAULT_COLUMNS 80
  69.  
  70. int do_not_echo = FALSE;        /* turn off echo on passwords */
  71.  
  72. static int keyboard_inited = FALSE;    /* Initialized yet? */
  73.  
  74. static int num_columns;            /* Number of columns on terminal. */
  75. static int current_column;        /* Current input column (0->nc). */
  76. static string keyboard_buffer;        /* Buffer for current input. */
  77. static string recall_buffer;        /* Buffer for previous input. */
  78. static int keyboard_pos;        /* Characters entered so far. */
  79. static int recall_pos;            /* Characters on previous input. */
  80. static int allow_intr;            /* Should we quit on INTR? */
  81. static int recall_pending;        /* Flag: recall previous string. */
  82. static int do_not_save_recall;        /* Flag: don't save recall buffer. */
  83.  
  84. static char k_delete, k_delete_2;    /* Backspace character(s). */
  85. static char k_erase, k_erase_2;        /* Erase-line character(s). */
  86. static char k_word;            /* Erase-word character. */
  87. static char k_refresh;            /* Refresh-line character. */
  88.  
  89. sigint_handler()            /* Restore terminal state; cleanup. */
  90. {
  91.   if (allow_intr)            /* Don't exit if this is disabled. */
  92.     die("\n\nInterrupt, exiting.\n");    /* Exit through error handler. */
  93. }
  94.  
  95. #ifndef XENIX
  96. sigtstp_handler()            /* Leave terminal alone! */
  97. {
  98.   flush_logfile();
  99.  
  100.   cooked_echo_mode();
  101.   kill(getpid(),SIGSTOP);
  102.   cbreak_noecho_mode();
  103.  
  104.   if (keyboard_pos != 0)        /* If there is input, we want to */
  105.     set_refresh_pending();        /* refresh when we start back up. */
  106. }
  107. #endif
  108.  
  109. preinit_keyboard()
  110. {
  111.   allow_interrupts();
  112.   no_use_stty();
  113.  
  114.   do_not_save_recall = FALSE;
  115.   recall_pending = FALSE;
  116.   recall_pos = 0;
  117. }
  118.  
  119. allow_interrupts()
  120. {
  121.   allow_intr = TRUE;
  122. }
  123.  
  124. disallow_interrupts()
  125. {
  126.   allow_intr = FALSE;
  127. }
  128.  
  129. #ifdef HPUX
  130.  
  131. use_stty()
  132. {
  133.   struct termio te_blob;
  134.   struct ltchars lt_blob;
  135.  
  136.   if (ioctl(0, TCGETA, &te_blob) == -1)
  137.     perror("TCGETA ioctl");
  138.  
  139.   if (ioctl(0, TIOCGLTC, <_blob) == -1)
  140.     perror("TIOCGLTC ioctl");
  141.  
  142.   k_delete = k_delete_2 = te_blob.c_cc[VERASE];
  143.   k_erase = k_erase_2 = te_blob.c_cc[VKILL];
  144.  
  145.   k_word = lt_blob.t_werasc;
  146.   k_refresh = lt_blob.t_rprntc;
  147. }
  148.  
  149. #else /* not HPUX */
  150. #ifdef SYSV
  151.  
  152. use_stty()
  153. {
  154.   struct termio tcblob;
  155.  
  156.   if (ioctl(0, TCGETA, &tcblob) == -1)
  157.     perror("TCGETA ioctl");
  158.  
  159.   k_delete = k_delete_2 = tcblob.c_cc[VERASE];
  160.   k_erase = k_erase_2 = tcblob.c_cc[VKILL];
  161.  
  162. #ifdef sgi
  163.  
  164.   k_word = tcblob.c_cc[VWERASE];
  165.   k_refresh = tcblob.c_cc[VRPRNT];
  166.  
  167. #else
  168.  
  169.   k_word = '\027';            /* Control-W. */
  170.   k_refresh = '\022';            /* Control-R. */
  171.  
  172. #endif /* sgi */
  173.  
  174. }
  175.  
  176. #else /* BSD */
  177.  
  178. use_stty()
  179. {
  180.   struct sgttyb sg_blob;
  181.   struct ltchars lt_blob;
  182.  
  183.   if (ioctl(0, TIOCGETP, &sg_blob) == -1)
  184.     perror("TIOCGETP ioctl");
  185.   if (ioctl(0, TIOCGLTC, <_blob) == -1)
  186.     perror("TIOCGLTC ioctl");
  187.  
  188.   k_delete = k_delete_2 = sg_blob.sg_erase;
  189.   k_erase = k_erase_2 = sg_blob.sg_kill;
  190.  
  191.   k_word = lt_blob.t_werasc;
  192.   k_refresh = lt_blob.t_rprntc;
  193. }
  194.  
  195. #endif /* SYSV */
  196. #endif /* HPUX */
  197.  
  198. no_use_stty()
  199. {
  200.   k_delete = '\010';            /* Backspace. */
  201.   k_delete_2 = '\177';            /* Delete. */
  202.  
  203.   k_erase = '\025';            /* Control-U. */
  204.   k_erase_2 = '\030';            /* Control-X. */
  205.  
  206.   k_word = '\027';            /* Control-W. */
  207.  
  208.   k_refresh = '\022';            /* Control-R. */
  209. }
  210.  
  211. init_keyboard()
  212. {
  213. #ifdef TIOCGWINSZ
  214.   struct winsize size;
  215. #endif
  216. #ifdef HPUX
  217.   struct sigvec hpvec;
  218. #endif
  219.  
  220.   cbreak_noecho_mode();
  221.  
  222. #ifdef TERMCAP
  223.   get_termcap_info();            /* Must precede TIOCGWINSZ code */
  224. #else
  225.   num_columns = DEFAULT_COLUMNS;    /* This is important! */
  226. #endif
  227.  
  228. #ifdef TIOCGWINSZ
  229.  
  230.   if (ioctl(0, TIOCGWINSZ, &size) == -1)
  231.     perror("TIOCGWINSZ ioctl");
  232.  
  233.   num_columns = size.ws_col;
  234.   if (num_columns <= 20)        /* Suspicious, reset it. */
  235.     num_columns = DEFAULT_COLUMNS;
  236.  
  237. #endif /* TIOCGWINSZ */
  238.  
  239.   set_default_wrap(num_columns);
  240.  
  241.   keyboard_pos = 0;            /* No input yet. */
  242.   current_column = 0;            /* Left side of screen. */
  243.  
  244. #ifdef HPUX
  245.  
  246.   hpvec.sv_handler = (void (*) ()) sigint_handler;
  247.   hpvec.sv_onstack = 0;
  248.   hpvec.sv_mask = 0;
  249.   sigvector(SIGINT,&hpvec,0);
  250.   hpvec.sv_handler = (void (*) ()) sigtstp_handler;
  251.   sigvector(SIGTSTP,&hpvec,0);
  252.  
  253. #else
  254. #ifdef XENIX
  255.  
  256.   signal(SIGINT, sigint_handler);    /* Control-C. */
  257.  
  258. #else /* BSD || SYSV */
  259.  
  260.   signal(SIGINT, sigint_handler);    /* Control-C. */
  261.   signal(SIGTSTP, sigtstp_handler);    /* Control-Z. */
  262.  
  263. #endif /* XENIX, BSD || SYSV */
  264. #endif /* HPUX */
  265.  
  266.   keyboard_inited = TRUE;
  267. }
  268.  
  269. #ifdef TERMCAP
  270.  
  271. get_termcap_info()
  272. {
  273.   char blob[1024];            /* As per termcap man page */
  274.   char *terminal_name, *temp;
  275.  
  276.   terminal_name = getenv("TERM");
  277.   if (terminal_name == NULL) {
  278.     have_clear = FALSE;
  279.     return;
  280.   }
  281.  
  282.   if (tgetent(blob, terminal_name) == 1) {
  283.     num_columns = tgetnum("co");    /* Try to get # of columns. */
  284.     if (num_columns == -1)        /* Not specified? */
  285.       num_columns = DEFAULT_COLUMNS;
  286.  
  287.     temp = start_of_line; 
  288.     if (tgetstr("cr",&temp) == NULL) {
  289.       start_of_line[0] = '\r';
  290.       start_of_line[1] = '\0';
  291.     }
  292.     temp = clear_to_eol;
  293.     if (tgetstr("ce",&temp) == NULL)
  294.       have_clear = FALSE;
  295.     else
  296.       have_clear = TRUE;
  297.   }
  298.   else                    /* Couldn't read termcap entry */
  299.     have_clear = FALSE;
  300. }
  301.  
  302. #endif
  303.  
  304. cleanup_keyboard()
  305. {
  306.   cooked_echo_mode();
  307. #ifndef XENIX
  308.   signal(SIGTSTP, NULL);
  309. #endif
  310. }
  311.  
  312. handle_keyboard_input()            /* Read input, one char at a time. */
  313. {
  314.   char ch_noreg;
  315.   register char ch;
  316.   int count;
  317.  
  318.   count = read(0, &ch_noreg, 1);    /* Read from stdin. */
  319.   if (count == 0)            /* Huh?  Ignore this. */
  320.     return;
  321.  
  322.   if (count == -1) {
  323.     if (errno == EWOULDBLOCK)
  324.       return;
  325.     else
  326.       die("%% Couldn't read keyboard.\n");
  327.   }
  328.  
  329.   ch = ch_noreg;            /* For stupid compilers. */
  330.  
  331.   if (ch == '\n') {
  332.     if (do_not_echo) {
  333.         do_not_echo = FALSE;
  334.     }
  335.     write(1, &ch_noreg, 1);
  336.     process_buffer();
  337.     if (recall_pending) {
  338.       recall_pending = FALSE;
  339.  
  340.       keyboard_pos = recall_pos;
  341.       bcopy(recall_buffer, keyboard_buffer, recall_pos); /* Retrieve buffer. */
  342.       current_column = keyboard_pos % num_columns;
  343.  
  344.       refresh_entire_buffer();
  345.     }
  346.     else {
  347.       keyboard_pos = 0;
  348.       current_column = 0;
  349.     }
  350.   }
  351.   else {
  352.     if ((ch == k_delete) || (ch == k_delete_2)) /* Backspace. */
  353.       handle_backspace();
  354.     else if ((ch == k_erase) || (ch == k_erase_2)) { /* Cancel line. */
  355.       keyboard_pos = 0;
  356.  
  357.       erase_keyboard_input(TRUE);
  358.       clear_refresh_pending();
  359.  
  360.       current_column = 0;
  361.     }
  362.     else if (ch == k_word) {        /* Erase word (back to space). */
  363.                     /* This is very, very, VERY ugly. */
  364.  
  365.       handle_backspace();        /* Always delete at least one char. */
  366.  
  367.       while ((keyboard_pos != 0) && (keyboard_buffer[keyboard_pos-1] != ' '))
  368.     handle_backspace();
  369.     }
  370.     else if (ch == k_refresh) {        /* Refresh entire input. */
  371.       refresh_entire_buffer();
  372.     }
  373.     else if ((ch < ' ') || (keyboard_pos == MAXSTRLEN - 1)) { /* Beep! */
  374.       write(1, "\007", 1);
  375.     }
  376.     else {
  377.       keyboard_buffer[keyboard_pos++] = ch;
  378.       current_column = (current_column + 1) % num_columns;
  379.       if (!do_not_echo)
  380.           write(1, &ch_noreg, 1);
  381.     }
  382.   }
  383. }
  384.  
  385.  
  386. handle_backspace()            /* Do a backspace.... */
  387. {
  388.   if (keyboard_pos != 0) {
  389.     keyboard_pos--;
  390.  
  391.     if (current_column != 0) {        /* Can do BS, SPC, BS. */
  392.       write(1, "\010 \010", 3);
  393.       current_column--;
  394.     }
  395.     else {
  396.       current_column = num_columns - 1;    /* Wrapped around. */
  397.       do_refresh();            /* Will redraw the right part. */
  398.     }
  399.   }
  400. }
  401.  
  402. process_buffer()
  403. {
  404.   register int kp = keyboard_pos;    /* Help the compiler optimize.       */
  405.                     /* keyboard_pos is static, and       */
  406.                     /* the compiler doesn't know if       */
  407.                     /* transmit() or log_input() might */
  408.                     /* change it (they don't).       */
  409.  
  410.   if (kp == 0)                /* Nothing typed. */
  411.     return;
  412.  
  413.   if (keyboard_buffer[0] == '/') {
  414.     if (keyboard_buffer[1] == '/') {    /* Two slashes, pass a single one. */
  415.       keyboard_buffer[kp] = '\n';
  416.       transmit(keyboard_buffer + 1, kp);
  417.       keyboard_buffer[kp] = '\0';
  418.       log_input(keyboard_buffer + 1);
  419.     }
  420.     else {                /* Single slash, it's a command. */
  421.       keyboard_buffer[kp] = '\0';
  422.       handle_command(keyboard_buffer, FALSE);
  423.       log_input(keyboard_buffer);    /* Should this be logged? */
  424.     }
  425.   }
  426.   else {
  427.     keyboard_buffer[kp] = '\n';
  428.     transmit(keyboard_buffer, kp + 1);
  429.     keyboard_buffer[kp] = '\0';
  430.     log_input(keyboard_buffer);
  431.   }
  432.  
  433.   if (!do_not_save_recall) {        /* Don't destroy buffer on /RECALL! */
  434.     recall_pos = kp;
  435.     bcopy(keyboard_buffer, recall_buffer, kp); /* Save it away. */
  436.   }
  437.   else
  438.     do_not_save_recall = FALSE;
  439. }
  440.  
  441. refresh_entire_buffer()            /* Refresh all lines of buffer. */
  442. {
  443.   register int i;
  444.  
  445.   erase_keyboard_input(TRUE);        /* Start at beginning of line. */
  446.                     /* Dump full lines. */
  447.   for (i = 0; i <= keyboard_pos - num_columns; i += num_columns) {
  448.     write(1, keyboard_buffer+i, num_columns);
  449.     write(1, "\n", 1);
  450.   }
  451.  
  452.   do_refresh();            /* Handle possibly partial tail. */
  453. }
  454.  
  455. do_refresh()
  456. {
  457.   int loc;                /* Place to start refresh from */
  458.   int modulo;
  459.  
  460.   modulo = keyboard_pos % num_columns;    /* Number on last line */
  461.   loc = keyboard_pos - modulo;
  462.  
  463.   if (modulo != 0)
  464.     write(1, &keyboard_buffer[loc], modulo);
  465.  
  466.   clear_refresh_pending();
  467. }
  468.  
  469. #ifdef TERMCAP
  470. write_one(c)
  471.   char c;
  472. {
  473.   *(write_buffer_ptr++) = c;
  474. }
  475. #endif
  476.  
  477. erase_keyboard_input(forced)
  478.   int forced;                /* Forced output? */
  479. {
  480.   if ((!forced) && (current_column == 0))
  481.     return;
  482.  
  483. #ifdef TERMCAP
  484.  
  485.   if (have_clear) {            /* Can we use termcap now? */
  486.     write_buffer_ptr = write_buffer;
  487.     tputs(start_of_line, 1, write_one);
  488.     tputs(clear_to_eol, 1, write_one);
  489.     write(1, write_buffer, write_buffer_ptr - write_buffer);
  490.     goto end_termcap;
  491.   }
  492.  
  493.   /* Drops through into non-TERMCAP code otherwise... */
  494.  
  495. #endif
  496.  
  497.   {
  498.     register int temp;
  499.     string buffer;            /* Should be long enough for 80*3. */
  500.     register char *ptr;
  501.  
  502.     temp = current_column;
  503.     ptr = buffer;
  504.  
  505.     while (temp != 0) {            /* Erase line, backwards. */
  506.       *(ptr++) = '\010';
  507.       *(ptr++) = ' ';
  508.       *(ptr++) = '\010';
  509.       temp--;
  510.     }
  511.     write(1, buffer, ptr - buffer);
  512.   }
  513.  
  514. end_termcap:
  515.  
  516.   if (keyboard_pos != 0)        /* If there is input, schedule a */
  517.     set_refresh_pending();        /* refresh event. */
  518.  
  519. }
  520.  
  521. avoid_recall()
  522. {
  523.   do_not_save_recall = TRUE;
  524. }
  525.  
  526. do_keyboard_recall()
  527. {
  528.   if (!keyboard_inited)            /* Dummy, nothing to recall yet! */
  529.     return;
  530.  
  531.   recall_pending = TRUE;
  532.   do_not_save_recall = TRUE;
  533. }
  534.